/*
 * logger.cpp
 *
 *  Created on: Apr 12, 2013
 *      Author: nick
 */

#include "hpp/logger.hpp"

namespace Logging {
	std::ostream & operator<<(std::ostream & out, const Level level) {
		static const char * const LEVELS_AS_STRING[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
		out << LEVELS_AS_STRING[level];
		return out;
	}
	LogMessage::LogMessage(const std::string & text, const Level severityLevel) : text(text), severityLevel(severityLevel) {
		time_t t = time(0);
		creationTime = ctime(&t);
		creationTime[strlen(creationTime) - 1] = '\0';
	}
	Level LogMessage::getSeverityLevel() const {
		return severityLevel;
	}
	const std::string & LogMessage::getText() const {
		return text;
	}
	const char* LogMessage::getCreationTime() const {
		return creationTime;
	}

	std::ostream & operator<<(std::ostream & out, const LogMessage & message) {
		static const int MAX_LEVEL_WIDTH = 10;
		static const int MAX_DATE_WIDTH = 30;
		out << std::left << std::setw(MAX_LEVEL_WIDTH) << message.severityLevel << std::left << std::setw(MAX_DATE_WIDTH) << message.creationTime << message.text;
		return out;
	}

	Logger::Logger() : destination(new LocalDestination()), severityLevel(INFO) {}
	Logger & Logger::getInstance() {
		static Logger logger;
		return logger;
	}
	void Logger::setDestination(Destination * destination) {
		this->destination = destination;
	}
	void Logger::setSeverityLevel(const Level level) {
		this->severityLevel = severityLevel;
	}
	void Logger::log(const LogMessage & message) {
		if(message.getSeverityLevel() >= this->severityLevel)
			destination->send(message);
	}
	Logger::~Logger() {}


	LocalDestination::LocalDestination(std::ostream & output) : output(&output), isFile(false) {}
	LocalDestination::LocalDestination(const char* fileName) : output(new std::ofstream(fileName, std::ios::out | std::ios::app)), isFile(true) {
		if(!((std::ofstream*)output)->is_open()) {
			throw IOException(std::string("Can't open log file : ") + fileName);
		}
	}
	void LocalDestination::send(const Logging::LogMessage & message) {
		*output << message << std::endl;
		output->flush();
	}
	LocalDestination::~LocalDestination() {
		if(isFile) {
			((std::ofstream*)output)->close();
			delete output;
		}
	}

	RemoteDestination::RemoteDestination() {
		throw NotImplementedException("class RemoteDestination not implemented yet");
	}

	void trace(const std::string & text) {
		Logger::getInstance().log(LogMessage(text, TRACE));
	}
	void debug(const std::string & text) {
		Logger::getInstance().log(LogMessage(text, DEBUG));
	}
	void info(const std::string & text) {
		Logger::getInstance().log(LogMessage(text, INFO));
	}
	void warn(const std::string & text) {
		Logger::getInstance().log(LogMessage(text, WARN));
	}
	void error(const std::string & text) {
		Logger::getInstance().log(LogMessage(text, ERROR));
	}

	void fatal(const std::string & text) {
		Logger::getInstance().log(LogMessage(text, FATAL));
	}
}
